/* 
 * File:   GUI.cpp
 * Created on 17. Juni 2011, 09:14
 */

#include "GUI.h"

GUI::GUI() : imExport(PlayListImExport("playlist.txt")) {
    mainWindow = 0;
    appName = "Player";
}

/**
 * createTrackbar()
 * Slider für Anzeige der abgespielten Zeit bzw. Play-Position.
 * @param hwnd          Handler des Fensters bzw. Elternelements
 * @param x             X-Position
 * @param y             Y-Position
 * @param width         Breite
 * @param height        Höhe
 */
void GUI::createTrackbar(HWND hwnd, int x, int y, int width, int height) {
    slider = CreateWindow(TRACKBAR_CLASS,
            "Trackbar Control",
            WS_CHILD | WS_VISIBLE | TBS_NOTICKS | TBS_ENABLESELRANGE,
            x, y, width, height,
            hwnd,
            (HMENU) ID_PLAYPOSITION,
            g_hInst,
            NULL
            );
}

/**
 * createVolumeSlider()
 * Lautsterkenregler erstellen.
 * @param hwnd          Handler des Fensters bzw. Elternelements
 * @param x             X-Position
 * @param y             Y-Position
 * @param width         Breite
 * @param height        Höhe
 */
void GUI::createVolumeSlider(HWND hwnd, int x, int y, int width, int height) {
    volume = CreateWindow(TRACKBAR_CLASS,
            "Trackbar Control",
            WS_CHILD | WS_VISIBLE | TBS_NOTICKS | TBS_ENABLESELRANGE | TBS_HORZ,
            x, y, width, height,
            hwnd,
            (HMENU) ID_VOLUME,
            g_hInst,
            NULL
            );
    SendMessage(volume, TBM_SETRANGE, TRUE, MAKELONG(0, 10));
    SendMessage(volume, TBM_SETPOS, (WPARAM) TRUE, (LPARAM) 10);
}

/**
 * createWindow()
 * Haupt-Fenster 600x200 erstellen.
 * @param hInstance Handle der aktuellen Instanz. Ist bei WIN32-Anwendungen immer deren Ladeadresse und besitzt den in der Regel den Wert 0x400000.
 */
void GUI::createWindow(HINSTANCE hInstance) {
    mainWindow = CreateWindow(getAppName(),
            "Mediaplayer",
            WS_OVERLAPPEDWINDOW,
            CW_USEDEFAULT, /* X-Position auf dem Monitor */
            CW_USEDEFAULT, /* Y-Position auf dem Monitor */
            600, /* Fensterbreite              */
            200, /* Fensterhoehe               */
            NULL,
            NULL,
            hInstance,
            NULL);
}

/**
 * createLabel()
 * Anzeige der abgespielten Datei erstellen
 * @param hwnd          Handler des Fensters bzw. Elternelements
 * @param x             X-Position
 * @param y             Y-Position
 * @param width         Breite
 * @param height        Höhe
 */
void GUI::createLabel(HWND hwnd, int x, int y, int width, int height) {
    label = CreateWindow("Static", " ",
            WS_CHILD | WS_VISIBLE,
            x, y, width, height,
            hwnd,
            (HMENU) ID_LABEL,
            g_hInst,
            NULL);
}

/**
 * createButton()
 * Button mit gewünschtem Text, Position, Größe und ID erstellen.
 * @param btnText       Button-Text
 * @param hwnd          Handler des Fensters bzw. Elternelements
 * @param x             X-Position
 * @param y             Y-Position
 * @param width         Breite
 * @param height        Höhe
 * @param id            ID des Buttons
 */
void GUI::createButton(const char* btnText, HWND hwnd, int x, int y, int width, int height, const ELEMENT_ID& id) {
    ELEMENT_ID element = id;
    CreateWindow("Button", btnText,
            BS_PUSHBUTTON | WS_CHILD | WS_VISIBLE,
            x, y, width, height,
            hwnd,
            (HMENU) element,
            g_hInst,
            NULL);
}

/**
 * createListbox()
 * Liste der Dateien erstellen. Grafische Darstellung der Wiedergabeliste.
 * @param hwnd  Handler des Fensters bzw. Elternelements
 */
void GUI::createListbox(HWND hwnd) {
    listbox = CreateWindow("LISTBOX", NULL,
            WS_CHILD | WS_VISIBLE | WS_BORDER | WS_VSCROLL | LBS_NOTIFY,
            220, 10, 330, 100,
            hwnd,
            (HMENU) ID_LIST,
            g_hInst,
            NULL);
}

/**
 * getMainWindow()
 * Hauptfenster
 * @return mainWindow
 */
HWND GUI::getMainWindow() {
    return mainWindow;
}

/**
 * getAppName()
 * Name der Applikation
 * @return appName
 */
const char* GUI::getAppName() {
    return appName;
}

/**
 * updateElements()
 * Folgende Elemente werden aktualisiert:
 * 1. Selektierung der aktuell abgespielten Datei in der Wiedergabeliste
 * 2. Anzeige der abgespielten Datei aktualisieren
 * 3. Die Skalierung des Zeit-Sliders aktualisieren
 */
void GUI::updateElements() {
    SendMessage(listbox, LB_SETCURSEL, (WPARAM) playlist.getSelectedIndex(), (LPARAM) 0);
    SetWindowText(label, getFileNameFromPath(playlist.getSelectedElement().c_str()));
    SendMessage(slider, TBM_SETRANGE, TRUE, MAKELONG(0, (int) player.getPlayLength() / (600)));
}

/**
 * openFile()
 * Datei-Auswahl-Dialog zum Auswählen der Datei, welche anschließend in die 
 * Variable openFileName gespeichert wird. Der Code wurde den Beispielen 
 * und Dokumentationen aus Folgenden Quellen entnommen.
 * Dokumentation: http://msdn.microsoft.com/en-us/library/ms646927%28VS.85%29.aspx
 * Common Item Dialog: http://msdn.microsoft.com/en-us/library/bb776913%28v=VS.85%29.aspx
 * Folgendem Beispiel entnomen: http://www.daniweb.com/software-development/cpp/code/217307
 * Mehr Beispiele: http://www.daniweb.com/software-development/cpp/threads/71778
 * @param hwnd  Handler des Fensters bzw. Elternelements
 */
void GUI::openFile(HWND hwnd) {
    ZeroMemory(&openFileName, sizeof (openFileName));
    openFileName.lStructSize = sizeof (openFileName);
    openFileName.hwndOwner = hwnd;
    openFileName.lpstrFile = szFile;
    openFileName.lpstrFile[0] = '\0';
    openFileName.nMaxFile = sizeof (szFile);
    openFileName.lpstrFilter = "All\0*.*\0Text\0*.TXT\0";
    openFileName.nFilterIndex = 1;
    openFileName.lpstrFileTitle = NULL;
    openFileName.nMaxFileTitle = 0;
    openFileName.lpstrInitialDir = NULL;
    openFileName.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST;
    GetOpenFileName(&openFileName);
}

/**
 * play()
 * Mehrere Funktionen, die zum Abspielen einer Datei gehören, wurden hier 
 * in einer Funktion zusammengefasst. Grund dafür ist die doppelte Verwendung 
 * der Reihe der Funktionen beim klick auf den Play-Button und Tastatur-Key.
 * @param hwnd  Handler des Fensters bzw. Elternelements
 */
void GUI::play(HWND hwnd) {
    if (playlist.getLength() > 0) {
        SetTimer(hwnd, ID_TIMER, 40, NULL);
        bool valid = player.play(playlist.getSelectedElement().c_str());
        if (valid)
            updateElements();
        SetFocus(hwnd);
    }

}

/**
 * pause()
 * Ebenso wie bei play-Funktion werden diese Funktionen doppelt verwendet und
 * wurden aus diesem Grund in eine Funktion zusammengefasst.
 * @param hwnd  Handler des Fensters bzw. Elternelements
 */
void GUI::pause(HWND hwnd) {
    player.pause();
    KillTimer(hwnd, ID_TIMER);
    SetFocus(hwnd);
}

/**
 * stop()
 * Ebenso wie bei play-Funktion werden diese Funktionen doppelt verwendet und
 * wurden aus diesem Grund in eine Funktion zusammengefasst.
 * @param hwnd  Handler des Fensters bzw. Elternelements
 */
void GUI::stop(HWND hwnd) {
    player.stop();
    KillTimer(hwnd, ID_TIMER);
    SetFocus(hwnd);
}

/**
 * switchToNext()
 * Ebenso wie bei play-Funktion werden diese Funktionen doppelt verwendet und
 * wurden aus diesem Grund in eine Funktion zusammengefasst.
 * @param hwnd  Handler des Fensters bzw. Elternelements
 */
void GUI::switchToNext(HWND hwnd) {
    if (playlist.getLength() > 0) {
        player.stop();
        SetTimer(hwnd, ID_TIMER, 40, NULL);
        bool valid = player.play(playlist.switchToNext().c_str());
        if (valid)
            updateElements();
        SetFocus(hwnd);
    }
}

/**
 * switchToPrevious()
 * Ebenso wie bei play-Funktion werden diese Funktionen doppelt verwendet und
 * wurden aus diesem Grund in eine Funktion zusammengefasst.
 * @param hwnd  Handler des Fensters bzw. Elternelements
 */
void GUI::switchToPrevious(HWND hwnd) {
    if (playlist.getLength() > 0) {
        player.stop();
        SetTimer(hwnd, ID_TIMER, 40, NULL);
        bool valid = player.play(playlist.switchToPrevious().c_str());
        if (valid)
            updateElements();
        SetFocus(hwnd);
    }
}

/**
 * windowEventhandler()
 * Eventhandler für alle Interaktionen im Haupt-Fenster.
 * @param hwnd     Handler des Fensters bzw. Elternelements
 * @param message  Vom Handler gelieferte Rückmeldung
 * @param wParam   Zusätzliche Nachrichteninformation vom Handler
 * @param lParam   Zusätzliche Nachrichteninformation vom Handler
 * @return DefWindowProc(hwnd, message, wParam, lParam)
 */
LRESULT CALLBACK GUI::windowEventhandler(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) {
    switch (message) {
        /**
         * Reagiert auf Tastatureingaben und bietet somit die Möglichkeit
         * den MediaPlayer per Tastatur zu bedienen.         
         */
        case WM_KEYDOWN:
        {
            switch (wParam) {
                case VK_RETURN: play(hwnd);
                    break;
                case VK_SPACE: pause(hwnd);
                    break;
                case VK_END: stop(hwnd);
                    break;
                case VK_LEFT: switchToPrevious(hwnd);
                    break;
                case VK_RIGHT: switchToNext(hwnd);
                    break;
            }
        }
            break;
        // WM_CREATE: Hier werden die grafischen Elemente erstellt.
        case WM_CREATE:
        {
            createLabel(hwnd, 10, 10, 200, 20);
            createTrackbar(hwnd, 10, 40, 200, 22);
            createVolumeSlider(hwnd, 145, 73, 50, 20);
            createButton(">", hwnd, 10, 70, 20, 25, ID_PLAY);
            createButton("[]", hwnd, 35, 70, 20, 25, ID_STOP);
            createButton("||", hwnd, 60, 70, 20, 25, ID_PAUSE);
            createButton("|<", hwnd, 85, 70, 20, 25, ID_PREV);
            createButton(">|", hwnd, 110, 70, 20, 25, ID_NEXT);
            createListbox(hwnd);
            loadDefaultPlaylist();
            createButton("+", hwnd, 220, 120, 20, 25, ID_ADD);
            createButton("-", hwnd, 250, 120, 20, 25, ID_DELETE);                                
        }
            break;
        case WM_TIMER:
            // Aktualisiert die Wiedergabeposition.
            SendMessage(slider, TBM_SETPOS, (WPARAM) TRUE, (LPARAM) player.getPlayPosition() / (600));
            //Sobald das Ende eines Titels erreicht ist, wird der nächste Titel abgespielt.
            if (player.isMusicFinished()) 
                switchToNext(hwnd);
            break;
        case WM_PAINT:
            hdc = BeginPaint(hwnd, &ps);
            EndPaint(hwnd, &ps);
            return 0;
         // WM_HSCROLL: Interaktionen der beiden Slider
        case WM_HSCROLL:
            if ((HWND) lParam == slider) {
                position = SendMessage(slider, TBM_GETPOS, 0, 0);
                player.setPlayPosition(position * 600);
            } else if ((HWND) lParam == volume) {
                loudness = SendMessage(volume, TBM_GETPOS, 0, 0);
                player.setVolume(loudness / 10.0f);
            }
            break;
          //  WM_COMMAND: Hier werden alle Befehle, wie zum beispiel ein Klick auf einen Button, abgefangen. 
        case WM_COMMAND:
            // Doppelklick auf ein Listenelement der Wiedergabeliste abfangen
            if ((HWND) lParam == listbox) {
                if (HIWORD(wParam) == LBN_DBLCLK) {
                    stop(hwnd);
                    int itemIndex = (int) SendMessage(listbox, LB_GETCURSEL, (WPARAM) 0, (LPARAM) 0);
                    playlist.setPosition(itemIndex);
                    play(hwnd);
                    SetFocus(hwnd);
                }
            }
            // Buttonevents werde hier abgefangen
            switch (wParam) {
                case ID_PLAY:
                    play(hwnd);
                    break;
                case ID_STOP:
                    stop(hwnd);
                    break;
                case ID_PAUSE:
                    pause(hwnd);
                    break;
                case ID_PREV:
                    switchToPrevious(hwnd);
                    break;
                case ID_NEXT:
                    switchToNext(hwnd);
                    break;
                case ID_ADD:
                    openFile(hwnd);
                    playlist.addToListLast(openFileName.lpstrFile); // playlist
                    addToListLast(listbox, getFileNameFromPath((char*) openFileName.lpstrFile)); // gui-playlist
                    SetFocus(hwnd);
                    break;
                case ID_DELETE:
                    int itemIndex = (int) SendMessage(listbox, LB_GETCURSEL, (WPARAM) 0, (LPARAM) 0);
                    playlist.removeFromList(itemIndex);
                    SendMessage(listbox, LB_DELETESTRING, (WPARAM) itemIndex, (LPARAM) 0);
                    break;
                default: break;
            }
            break;
         // Fenster schließen
        case WM_CLOSE:
            imExport.exportPlayList(playlist.getList());
            DestroyWindow(hwnd);
            break;
        case WM_DESTROY:
            PostQuitMessage(0);
            return 0;
    }

    return DefWindowProc(hwnd, message, wParam, lParam);
}

/**
 * loadDefaultPlaylist()
 * Standard Wiedergabeliste laden.
 */
void GUI::loadDefaultPlaylist() {
    vector<string> pathes;
    imExport.importPlayList(pathes);
    for (int i = 0; i < pathes.size(); i++) {
        playlist.addToListLast(pathes.at(i).c_str());
        addToListLast(listbox, getFileNameFromPath(pathes.at(i).c_str()));
    }
}

/**
 * addToListLast()
 * Element in die Wiedergabeliste hinzufügen.
 * @param idnr          ID der Liste
 * @param ltext         Dateiname
 */
void GUI::addToListLast(HWND idnr, char* ltext) {
    SendMessage(idnr, (UINT) LB_ADDSTRING, (WPARAM) 0, (LPARAM) ltext);
}
